#include <bits/stdc++.h>
using namespace std;
const int N=4000005;
const int M=8000005;
const int INF=0x3f3f3f3f;
int n,m,en;
struct Edge{
    int v,w,next;
}edge[M];
int cnt,head[N];
void init(){
    cnt=0;
    memset(head,-1,sizeof(head));
}
void addEdge(int u,int v,int w){
    edge[cnt]=Edge{v,w,head[u]};
    head[u]=cnt++;
    edge[cnt]=Edge{u,w,head[v]};
    head[v]=cnt++;
}
int dis[N];
bool vis[N];
struct node{
    int v,w;
    bool operator <(node a)const{
        return w>a.w;
    }
}tmp;
int Dijkstra(int s,int t){
    for(int i=0;i<=en;i++){
        dis[i]=INF;
        vis[i]=false;
    }
    dis[s]=0;
    priority_queue<node> q;
    q.push(node{s,0});
    while(!q.empty()){
        //printf("Fd\n");
        tmp=q.top();
        q.pop();
        int u=tmp.v;
        vis[u]=true;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            int w=edge[i].w;
            //printf("%d %d %d\n",u,v,w);
            if(!vis[v] && dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                q.push(node{v,dis[v]});
            }
        }
    }
    return dis[t];
}
inline int number(int x,int y,bool up)//每个方格以左上角坐标表示
{
    int ret = ((m-1)*(x-1)+y-1)*2+1;
    if(up) ret++;
    return ret; // S:0 T:1
}
int main(void){
    //freopen("data.txt","r",stdin);
    scanf("%d%d",&n,&m);
    en=2*(n-1)*(m-1)+1;
    int t;
    //特判
    if(n==1 && m==1){
        printf("0\n");
    }else if(n==1){
        int ans=INF;
        for(int i=1;i<m;i++){
            scanf("%d",&t);
            ans=min(ans,t);
        }
        printf("%d\n",ans);
    }else if(m==1){
        int ans=INF;
        for(int i=1;i<n;i++){
            scanf("%d",&t);
            ans=min(ans,t);
        }
        printf("%d\n",ans);
    }else{
        init();
        //横边
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m-1;j++){
                scanf("%d",&t);
                if(i==1){
                    //最上面一条直线的边，转化为第一层偶数平面和最上面平面end的边
                    addEdge(2*j,en,t);
                }else if(i==n){
                    //最下面一条直线的边，转化为最后一层平面和最下面平面0的边
                    //i就是当前层数，减去2就是第一层还有最后一层，中间的层数乘以每层有2(m-1)个平面
                    //再加2*j-1就是当前最后一层平面的编号，连上0
                    addEdge(0,(i-2)*2*(m-1)+2*j-1,t);
                }else{
                    //中间的边连上面一层的下平面和下面一层的上平面
                    addEdge(2*(m-1)*(i-1)+2*j,2*(i-2)*(m-1)+2*j-1,t);
                }
            }
        }
        //竖边，类似的处理方式
        for(int i=1;i<=n-1;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&t);
                if(j==1){
                    addEdge(0,(i-1)*(m-1)*2+1,t);
                }else if(j==m){
                    //printf("%d %d\n",i*(m-1)*2,number(i,j-1,true));
                    addEdge(i*(m-1)*2,en,t);
                }else{
                    addEdge((i-1)*(m-1)*2+(j-1)*2,(i-1)*(m-1)*2+2*j-1,t);
                }
            }
        }
        //斜边
        for(int i=1;i<=n-1;i++){
            for(int j=1;j<=m-1;j++){
                scanf("%d",&t);
                //printf("%d %d\n",(i-1)*2*(m-1)+2*(j-1)+1,(i-1)*2*(m-1)+2*j);
                addEdge((i-1)*2*(m-1)+2*(j-1)+1,(i-1)*2*(m-1)+2*j,t);
            }
        }
        // for(int i=0;i<=en;i++){
        //     for(int j=head[i];j!=-1;j=edge[j].next){
        //         int v=edge[j].v;
        //         int w=edge[j].w;
        //         printf("%d %d %d\n",i,v,w);
        //     }
        // }
        int ans=Dijkstra(0,en);
        printf("%d\n",ans);
    }
    return 0;
}